import datetime
import math
import random
import time

from selenium.common import NoSuchElementException
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By

from libs import config
from libs.action import WebDriverAction
from libs.game import Game
from libs.image import ImageTool
from libs.log import Log
from libs.movement import Movement
from libs.trade_helper import TradeHelper


class FarmTool:
    def __init__(self, shears_index, water_can_index, seeds):
        self.shears_index = shears_index
        self.water_can_index = water_can_index
        self.seeds = seeds


class FarmHelper:
    def __init__(self, driver, user_id, farm_item_key, soil_id):
        self.driver = driver
        self.game = Game(driver, user_id)
        self.action = WebDriverAction(driver)
        self.image_tool = ImageTool(driver)
        self.movement = Movement(driver)
        self.log = Log(user_id)
        self.farm_item = config.get_farm_items().get(farm_item_key)
        self.user_id = user_id
        self.trade_helper = TradeHelper(driver, user_id)

        self.farm_tool = None
        self.round_stop = False
        self.soil_id = soil_id

    def goto_farm_position(self):
        # 走到位置
        log = self.log
        movement = self.movement
        sid = self.soil_id
        log.debug('goto farm position')
        if 0 < sid < 20:
            farm_id = 3451
        elif 20 < sid < 40:
            farm_id = 3450
        elif 40 < sid < 60:
            farm_id = 1202
        else:
            raise Exception('invalid soil_id')
        self.enter_land(farm_id)
        movement.keep_move_up(3)
        movement.keep_move_up(3)
        is_top_row = False

        if farm_id == 3450:
            if 34 >= sid > 27:
                sid = sid - 7
                is_top_row = True
            if sid == 21 or sid == 22:
                # 22
                movement.keep_move_down(0.5)  # 下
                movement.keep_move_left(3)
                movement.keep_move_left(3)  # 左两次
                if sid == 21:
                    # 22->21
                    movement.keep_move_down(1)
                    movement.keep_move_down(1)  # 下两次
                    movement.keep_move_left(0.5)
                    movement.keep_move_left(0.5)
                    movement.keep_move_up(0.5)
                    movement.keep_move_left(1)
                    movement.keep_move_up(0.5)
                    movement.keep_move_left(1)
                    movement.keep_move_left(1)

            if sid == 23 or sid == 24:
                # 24
                movement.keep_move_left(0.5)
                movement.keep_move_left(0.5)  # 左两次
                if sid == 23:
                    # 24->23
                    movement.keep_move_down(1)
                    movement.keep_move_down(1)  # 下两次
                    movement.keep_move_left(1.5)
                    movement.keep_move_left(1.5)  # 左两次
                    movement.keep_move_up(1)  # 上

            if sid == 25 or sid == 26 or sid == 27 or sid == 35:
                # 25
                movement.keep_move_right(1)
                movement.keep_move_right(1)  # 右两次

                if sid == 26:
                    # 25->26
                    movement.keep_move_down(2)
                    movement.keep_move_down(2)  # 下两次
                    movement.move_up(1, 0.1)  # 上一步
                    movement.keep_move_right(2)
                    movement.keep_move_right(2)  # 右两次
                if sid == 27 or sid == 35:
                    # 25 -> 27
                    movement.move_down(1, 0.2)  # 下一步
                    movement.keep_move_right(3)
                    movement.keep_move_right(3)  # 右两次
                    if sid == 35:
                        # 27->35
                        movement.keep_move_up(1.1)
                        movement.keep_move_right(1)
                        movement.keep_move_right(1)

        if farm_id == 3451:
            if 12 >= sid > 6:
                sid = sid - 6
                is_top_row = True

            time.sleep(0.5)
            if sid == 1 or sid == 13:
                movement.keep_move_down(0.3)
                movement.keep_move_left(1.5)
                movement.keep_move_down(1)
                movement.keep_move_left(2)
                movement.keep_move_up(0.3)
                movement.keep_move_left(3)
                time.sleep(0.5)
                if sid == 13:
                    movement.keep_move_up(2.1)
                    movement.keep_move_left(7)
                    movement.keep_move_up(0.5)
                    movement.keep_move_left(3)
            if sid == 2:
                movement.keep_move_down(0.3)
                movement.keep_move_left(1.5)
                movement.keep_move_down(1)
                movement.keep_move_left(2)

            if sid == 3:
                movement.keep_move_left(1)
                movement.keep_move_left(1)
            if sid == 4 or sid == 5 or sid == 6 or sid == 14:
                movement.keep_move_right(1.2)
                time.sleep(0.5)
                if sid == 5 or sid == 6 or sid == 14:
                    movement.keep_move_down(0.2)
                    movement.keep_move_down(0.2)
                    time.sleep(0.5)
                    if sid == 5:
                        movement.keep_move_right(0.5)
                        movement.keep_move_down(0.2)
                        movement.keep_move_down(0.2)
                        movement.keep_move_right(2)
                        time.sleep(0.5)
                    if sid == 6 or sid == 14:
                        movement.keep_move_right(5)
                        movement.keep_move_right(5)
                        time.sleep(0.5)
                        if sid == 14:
                            movement.keep_move_up(1.5)
                            movement.keep_move_right(2)
                            movement.keep_move_right(2)

        if farm_id == 1202:
            if 54 >= sid > 47:
                sid = sid - 7
                is_top_row = True

            time.sleep(0.5)
            if sid == 43:
                movement.keep_move_left(3)
                movement.keep_move_left(3)
            if sid == 44 or sid == 45 or sid == 46 or sid == 47:
                movement.keep_move_right(0.2)
                movement.keep_move_right(0.2)
                if sid == 45 or sid == 46 or sid == 47:
                    movement.keep_move_down(0.2)
                    movement.keep_move_down(0.2)
                    movement.keep_move_right(3)
                    movement.keep_move_right(3)
                    if sid == 46 or sid == 47:
                        movement.keep_move_down(0.2)
                        movement.keep_move_down(0.2)
                        movement.keep_move_right(3)
                        movement.keep_move_right(3)
                        if sid == 47:
                            movement.keep_move_up(0.3)
                            movement.keep_move_right(3)
                            movement.keep_move_right(3)

        # 去上层
        if is_top_row:
            movement.keep_move_up(7)
            movement.keep_move_up(7)

            movement.keep_move_right(0.3)
            movement.keep_move_right(0.3)

            movement.keep_move_up(0.3)
            movement.keep_move_up(0.3)

            movement.keep_move_left(0.3)
            movement.keep_move_left(0.3)

    def enter_land(self, land_no):
        self.action.goto_land(land_no)
        # 检查进入后的位置
        door_loc = self.image_tool.find_target('land_door_icon.png')
        if door_loc is None:
            self.action.goto_spec()
            self.action.goto_land(land_no)
        else:
            x, y = door_loc
            if abs(x - 368) > 50 or abs(y - 438) > 50:
                self.action.goto_spec()
                self.action.goto_land(land_no)

    def rescan_seeds(self):
        self.action.escape()
        # 重新读取背包数量
        self.game.open_backpack()
        cell_items = self.driver.find_elements(By.CLASS_NAME, 'Hud_item__YGtIC')
        for index in self.farm_tool.seeds:
            try:
                cell_item = cell_items[index - 1]
                qty_elem = cell_item.find_element(By.CLASS_NAME, 'Hud_quantity__V_YWQ')
                ac = ActionChains(self.driver)
                ac.move_to_element(cell_item).perform()
                hover_title_elem = self.driver.find_element(By.XPATH,
                                                            "//span[@class='ItemStyles_tooltipTitle____kIs']")
                item_name = hover_title_elem.text
                if item_name != self.farm_item.get('seed_name'):
                    self.farm_tool.seeds[index] = 0
                    continue
                if qty_elem.text == '':
                    qty = 1
                else:
                    qty = int(qty_elem.text.replace('x', ''))
                self.farm_tool.seeds[index] = qty

            except NoSuchElementException:
                self.farm_tool.seeds[index] = 0
                continue
            except Exception:
                continue
        self.farm_tool.seeds = {k: v for k, v in self.farm_tool.seeds.items() if v != 0}
        self.game.close_backpack()

    def get_seed_index_and_qty(self, use_qty):
        self.action.escape()
        for index in self.farm_tool.seeds:
            qty = self.farm_tool.seeds.get(index, 0)
            if qty <= 0:
                continue
            self.farm_tool.seeds[index] = max(0, qty - use_qty)
            return index, qty
        return 0, 0

    def plant(self, start_x, start_y):
        ac = ActionChains(self.driver)
        body = self.driver.find_element(By.TAG_NAME, 'html')
        ac.reset_actions()
        ac.move_by_offset(start_x, start_y)
        delta_x = 62
        delta_y = 62
        count_x = 5
        count_y = 5
        seed_qty = 0
        for _ in range(count_x):
            for _ in range(count_y):
                if not self.game.in_game():
                    raise Exception('not in game')
                if seed_qty <= 0:
                    seed_index, seed_qty = self.get_seed_index_and_qty(25)
                    if seed_qty <= 0:
                        body.send_keys(Keys.ESCAPE)
                        return False
                    self.game.use_backpack_item(seed_index)
                ac.click().perform(),
                seed_qty = seed_qty - 1
                time.sleep(random.randrange(3, 5) / 100)
                ac.move_by_offset(0, delta_y)
            ac.move_by_offset(delta_x, -count_y * delta_y).perform()  # 到下一列出发点
        body.send_keys(Keys.ESCAPE)
        return True

    def watering(self, start_x, start_y):
        self.game.use_backpack_item(self.farm_tool.water_can_index)
        ac = ActionChains(self.driver)
        body = self.driver.find_element(By.TAG_NAME, 'html')
        ac.reset_actions()
        ac.move_by_offset(start_x, start_y)
        delta_x = 62
        delta_y = 62
        count_x = 5
        count_y = 5
        for _ in range(count_x):
            for _ in range(count_y):
                if not self.game.in_game():
                    raise Exception('not in game')
                ac.click().perform(),
                time.sleep(random.randrange(3, 5) / 100)
                ac.move_by_offset(0, delta_y)
            ac.move_by_offset(delta_x, -count_y * delta_y).perform()  # 到下一列出发点
        body.send_keys(Keys.ESCAPE)

    def collect(self, start_x, start_y):
        self.game.use_backpack_item(self.farm_tool.shears_index)
        ac = ActionChains(self.driver)
        body = self.driver.find_element(By.TAG_NAME, 'html')
        ac.reset_actions()
        ac.move_by_offset(start_x, start_y)
        delta_x = 62
        delta_y = 62
        count_x = 5
        count_y = 5
        for _ in range(count_x):
            for _ in range(count_y):
                if not self.game.in_game():
                    raise Exception('not in game')
                ac.click().perform(),
                time.sleep(random.randrange(3, 5) / 100)
                ac.move_by_offset(0, delta_y)
            ac.move_by_offset(delta_x, -count_y * delta_y).perform()  # 到下一列出发点
        body.send_keys(Keys.ESCAPE)

    def init_farm_tool(self, seed_name, shears_name):
        backpack = self.game.backpack
        shears_index = 0
        water_can_index = 0
        seeds = {}

        for index in backpack:
            item = backpack.get(index)
            name = item.get('name')
            qty = item.get('qty')
            if name == shears_name:
                shears_index = index
            if name == seed_name:
                seeds[index] = qty
            if name == 'Rusty Watering Can':
                water_can_index = index
        self.farm_tool = FarmTool(shears_index, water_can_index, seeds)

    def get_wine(self):
        wines = {
            "Berry Blast": 100,
            "Popberry Wine": 95,
        }
        wine_name = None
        wine_qty = 0
        wine_index = None
        wine_energy = 0
        for name in wines:
            index, qty = self.game.get_backpack_index_and_qty(name)
            if qty > 0:
                wine_name = name
                wine_qty = qty
                wine_index = index
                wine_energy = wines.get(name)
                break
        return wine_name, wine_qty, wine_index, wine_energy

    def is_energy_enough(self, farm_qty):
        return self.game.get_energy() >= self.farm_item.get('energy_cost') * farm_qty + 10

    def prepare_farm(self):
        max_farm_count = 999
        self.game.get_backpack()
        wine_name, wine_qty, wine_index, wine_energy = self.get_wine()
        energy = int(wine_qty * wine_energy + self.game.get_energy())
        if energy < 80:
            return False
        # 根据体力判断需要购买的种子数量
        seed_name = self.farm_item.get('seed_name')
        energy_cost = self.farm_item.get('energy_cost')
        seed_qty = self.game.get_backpack_qty(seed_name)
        farm_count = int((energy - 10) / energy_cost)
        farm_count = min(max_farm_count, farm_count)
        self.log.debug(f"seed_qty={seed_qty}, energy={energy}, farm_count={farm_count}/{max_farm_count}")

        space_count = self.game.get_backpack_space()
        buy_count = farm_count - seed_qty
        if space_count - math.floor(buy_count / 99) <= 1:
            Log('notify').info(f'[{self.user_id}]: space({space_count}) not enough')
            return True
        if buy_count > 75:
            # 买种子
            self.log.debug('buy seed')
            self.trade_helper.goto_seed_store()
            time.sleep(2)
            self.trade_helper.open_seed_buy()
            time.sleep(1)
            self.trade_helper.buy_seed(seed_name, farm_count - seed_qty)
            time.sleep(1)
        self.action.escape()
        self.game.get_backpack()
        return True

    def farm(self):
        plant_times = {}
        is_done = False
        plant = True
        self.init_farm_tool(self.farm_item.get('seed_name'), self.farm_item.get('shear'))

        for farm_round in range(60):
            if not config.is_scheduler_running():
                self.log.debug('scheduler terminated, stop after clear energy')
                self.round_stop = True

            self.rescan_seeds()
            self.movement.keep_move_up(7)
            if not self.is_energy_enough(75):
                # 喝酒
                drink_loc = self.image_tool.find_nearest_target('plant_start.png', 0.8, 400, 500)
                if drink_loc is not None:
                    drink_x, drink_y = drink_loc
                    # 根据地板定位角色
                    self.drink_wine(1000, drink_x + 170, drink_y - 60)

            self.movement.keep_move_down(1)
            time.sleep(1)
            start_x = start_y = None
            for _ in range(3):
                start_loc = self.image_tool.find_nearest_target('soil_start.png', 0.8, 400, 500)
                if start_loc is not None:
                    start_x, start_y = start_loc
                    if abs(start_y - 435) < 150:
                        break
                self.movement.keep_move_up(7)
                self.movement.keep_move_down(1)
                time.sleep(1)
                continue

            if not self.is_energy_enough(75):
                plant = False
                is_done = True

            start_x = start_x - 100
            start_y = start_y + 90
            before_energy = self.game.get_energy()
            for loop in range(3):
                if loop == 1:
                    self.movement.keep_move_down(1.55)
                if loop == 2:
                    self.movement.keep_move_down(1.5)
                time.sleep(1)
                start_loc = self.image_tool.find_nearest_target('plant_start.png', 0.8, 340, 530)
                if start_loc is not None:
                    start_x, start_y = start_loc
                    start_x = start_x + 50
                    start_y = start_y + 35

                self.action.move_by_offset(start_x, start_y)
                time.sleep(0.5)
                bubble = self.action.find_element(By.CLASS_NAME, 'LookAtBubble_lookAtBubble___pd4a', None, 1)
                if farm_round != 0 or bubble is not None:
                    if loop in plant_times:
                        plant_time = plant_times.get(loop)
                        wait_seconds = max(0,
                                           self.farm_item.get('wait_seconds') - (
                                                       datetime.datetime.now() - plant_time).seconds)
                        time.sleep(wait_seconds)
                        del plant_times[loop]
                    self.collect(start_x, start_y)
                if plant:
                    plant = self.plant(start_x, start_y)
                    plant_times[loop] = datetime.datetime.now()
                    self.watering(start_x, start_y)

            if farm_round == 0 and before_energy - self.game.get_energy() < 50:
                self.log.debug('energy cost less than 50, stop farm')
                self.round_stop = True
                plant = False
            if not plant:
                if is_done:
                    break
                else:
                    is_done = True
        self.after_farm()

    def after_farm(self):
        self.game.get_backpack()
        self.log.debug('sell corp')
        self.trade_helper.goto_sell_position()
        self.trade_helper.open_shop_sell()
        item_name = self.farm_item.get('item_name')
        item_qty = self.game.get_backpack_qty(item_name)
        now = datetime.datetime.now()
        price_delta = -1
        if 8 < now.hour < 20:
            price_delta = 0
        self.trade_helper.sell_item(item_name, item_qty, min_price=self.farm_item.get('min_price'), price_delta=price_delta)
        self.action.escape()
        self.action.escape()
        time.sleep(10)

    def drink_wine(self, energy_target, role_x, role_y):
        wine_name, wine_qty, wine_index, wine_energy = self.get_wine()
        if wine_name is None:
            return

        action = WebDriverAction(self.driver)
        print(f"drink energy start: energy={self.game.get_energy()}, need {energy_target}")

        self.game.use_backpack_item(wine_index)
        before_drink_energy = self.game.get_energy()
        no_effect_count = 0
        for _ in range(20):
            if energy_target - self.game.get_energy() < wine_energy:
                print('energy full')
                action.escape()
                break
            # 点击角色喝酒
            self.action.click_by_offset(role_x, role_y)
            after_drink_energy = self.game.get_energy()
            if after_drink_energy - before_drink_energy > 10:
                before_drink_energy = after_drink_energy
                continue
            else:
                # 体力没增加
                print('drink no effect')
                no_effect_count += 1
                if no_effect_count > 2:
                    action.escape()
                    break
                continue
        print(f"drink energy end: energy={self.game.get_energy()}")
        time.sleep(1)
        self.action.escape()
